#include "jsapi.h"
#include "js-engine.h"
#include "desktopClasses.h"
#include "basicElementClass.h"
#include "mainview-parser.h"

static JSBool basicElement_new(JSContext *cx, JSObject *obj, uintN argc, jsval *args, jsval *rval);
static JSBool basicElement_appendElement(JSContext *cx, JSObject *obj, uintN argc, jsval *args, jsval *rval);
static JSBool basicElement_focus(JSContext *cx, JSObject *obj, uintN argc, jsval *args, jsval *rval);
static JSBool basicElement_insertElement(JSContext *cx, JSObject *obj, uintN argc, jsval *args, jsval *rval);
static JSBool basicElement_killFocus(JSContext *cx, JSObject *obj, uintN argc, jsval *args, jsval *rval);
static JSBool basicElement_removeElement(JSContext *cx, JSObject *obj, uintN argc, jsval *args, jsval *rval);
static JSBool basicElement_removeAllElements(JSContext *cx, JSObject *obj, uintN argc, jsval *args, jsval *rval);

JSBool basicElement_SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);

static gboolean element_button_event_cb(GtkWidget *widget, GdkEventButton *event, gpointer ptr);
static gboolean element_key_release_cb(GtkWidget *widget, GdkEventKey *event, gpointer ptr);
static gboolean element_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer ptr);
static gboolean element_mouse_crossing_cb(GtkWidget *widget, GdkEventCrossing *event, gpointer ptr);
static gboolean element_mouse_move_cb(GtkWidget *widget, GdkEventMotion *event, gpointer ptr);

struct JSFunctionSpec basicElement_methods[] = {
    {"appendElement", basicElement_appendElement, 1, 0, 0}, 
    {"focus", basicElement_focus, 0, 0, 0},
    {"insertElement", basicElement_insertElement, 2, 0, 0},
    {"killFocus", basicElement_killFocus, 0, 0, 0},
    {"removeElement", basicElement_removeElement, 1, 0, 0},
    {"removeAllElements", basicElement_removeAllElements, 0, 0, 0},
};

static JSClass basicElementClass = 
    { "basicElement", JSCLASS_HAS_PRIVATE,
      JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, basicElement_SetProperty,
      JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
      JSCLASS_NO_OPTIONAL_MEMBERS
    };

struct property_parser basicElement_parser[] = {
    {"cursor", set_property_string_value},
    {"dropTarget", set_property_boolean_value},
    {"enabled", set_property_boolean_value},
    {"height", set_property_integer_value},
    {"hitTest", set_property_string_value},
    {"mask", set_property_string_value},
    {"name", set_property_string_value},
    {"offsetHeight", set_property_integer_value},
    {"offsetWidth", set_property_integer_value},
    {"offsetX", set_property_integer_value},
    {"offsetY", set_property_integer_value},
    {"opacity", set_property_integer_value},
    {"pinX", set_property_integer_value},
    {"pinY", set_property_integer_value},
    {"rotation", set_property_float_value},
    {"tagName", set_property_string_value},
    {"tooltip", set_property_string_value},
    {"width", set_property_integer_value},
    {"visible", set_property_boolean_value},
    {"x", set_property_integer_value},
    {"y", set_property_integer_value},
    {NULL, NULL},
};

JSBool basicElement_SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    gchar *key;
    desktop_element *element = JS_GetPrivate(cx, obj);

    g_return_val_if_fail(JSVAL_IS_STRING(id), JS_FALSE);
    key = JS_GetStringBytes(JSVAL_TO_STRING(id));

    if(!element->view)
	return JS_TRUE;
	
    if(JSVAL_IS_BOOLEAN(*vp)){
	gboolean value = JSVAL_TO_BOOLEAN(*vp);
	if(g_ascii_strcasecmp(key, "visible") == 0){
	    if(value)
		gtk_widget_show_all(element->view);
	    else
		gtk_widget_hide_all(element->view);
	}
    }
    return JS_TRUE;	
}

static JSBool basicElement_appendElement(JSContext *cx, JSObject *obj, uintN argc, jsval *args, jsval *rval)
{
    g_return_val_if_fail(argc == 1, JS_FALSE);
    g_return_val_if_fail(JSVAL_IS_STRING(args[0]), JS_FALSE);
    g_message("basicElement_appendElement called. message : %s",JS_GetStringBytes(JSVAL_TO_STRING(args[0])));

    return JS_TRUE;
}

static JSBool basicElement_focus(JSContext *cx, JSObject *obj, uintN argc, jsval *args, jsval *rval)
{
    g_message("basicElement focus called.");
    return JS_TRUE;
}

static JSBool basicElement_insertElement(JSContext *cx, JSObject *obj, uintN argc, jsval *args, jsval *rval)
{
    g_return_val_if_fail(argc == 2, JS_FALSE);
    g_return_val_if_fail(JSVAL_IS_STRING(args[0]), JS_FALSE);
#if 0
    g_return_val_if_fail(JSVAL_IS_ELEMENT(args[1]), JS_FALSE);
#endif
    return JS_TRUE;
}

static JSBool basicElement_killFocus(JSContext *cx, JSObject *obj, uintN argc, jsval *args, jsval *rval)
{
    return JS_TRUE;
}

static JSBool basicElement_removeElement(JSContext *cx, JSObject *obj, uintN argc, jsval *args, jsval *rval)
{
    g_return_val_if_fail(argc == 1, JS_FALSE);
#if 0
    g_return_val_if_fail(JSVAL_IS_ELEMENT(args[1]), JS_FALSE);
#endif
    return JS_TRUE;
}

static JSBool basicElement_removeAllElements(JSContext *cx, JSObject *obj, uintN argc, jsval *args, jsval *rval)
{
    return JS_TRUE;
}

static JSBool basicElement_new(JSContext *cx, JSObject *obj, uintN argc, jsval *args, jsval *rval)
{
    g_message("basicElement new");
    return JS_TRUE;
}

JSObject *basicElementClass_init(js_engine *engine)
{
    JSObject *proto = 
	JS_InitClass(engine->cx, engine->glob, NULL, 
		     &basicElementClass, basicElement_new,
		     0,NULL,NULL,NULL,NULL);
    g_return_val_if_fail(proto, FALSE);
    g_return_val_if_fail(JS_DefineFunctions(engine->cx, proto, basicElement_methods), FALSE);
    return proto;
}

void set_element_event_cb(desktop_element *element)
{
    g_return_if_fail(element);
    g_signal_connect(element->view, "button-press-event", 
		     G_CALLBACK(element_button_event_cb), element);
    g_signal_connect(element->view, "button-release-event",
		     G_CALLBACK(element_button_event_cb), element);
    g_signal_connect(element->view, "key-press-event",
		     G_CALLBACK(element_key_press_cb), element);
    g_signal_connect(element->view, "key-release-event",
		     G_CALLBACK(element_key_release_cb), element);
    g_signal_connect(element->view, "motion-notify-event", 
		     G_CALLBACK(element_mouse_move_cb), element);
    g_signal_connect(element->view, "enter-notify-event",
		     G_CALLBACK(element_mouse_crossing_cb), element);
    g_signal_connect(element->view, "leave-notify-event",
		     G_CALLBACK(element_mouse_crossing_cb), element);

}

static gboolean element_button_event_cb(GtkWidget *widget, GdkEventButton *event, gpointer ptr)
{
    desktop_element *element = (desktop_element *)ptr;
    switch (event->button){
    case 1:{
	switch(event->type){
	case GDK_BUTTON_RELEASE:
	    desktop_element_call_handler(element, ONCLICK);
	    break;
	case GDK_2BUTTON_PRESS:
	    desktop_element_call_handler(element, ONDBLCLICK);
	    break;
	default:
	    break;
	}
	break;
    }
    case 3:{
	switch(event->type){
	case GDK_BUTTON_RELEASE:
	    desktop_element_call_handler(element, ONRCLICK);
	    break;
	case GDK_2BUTTON_PRESS:
	    desktop_element_call_handler(element, ONRDBLCLICK);
	    break;
	default:
	    break;
	}
	break;
    }
    }
    return FALSE;
}

static gboolean element_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer ptr)
{
    desktop_element *element = (desktop_element *)ptr;
    guint16 keyCode = event->hardware_keycode;
    guint state = event->state;
    jsval event_val;
    JS_GetProperty(element->engine->cx, element->engine->view, "event", &event_val);
    if(JSVAL_IS_OBJECT(event_val)){
        JSObject *event_obj = JSVAL_TO_OBJECT(event_val);
        jsval keyCode, 
            shiftKey = JSVAL_FALSE, 
            ctrlKey = JSVAL_FALSE, 
            altKey = JSVAL_FALSE;
        keyCode = INT_TO_JSVAL(event->keyval & 0x00ff);
        if(event->state & GDK_SHIFT_MASK)
	    shiftKey = JSVAL_TRUE; 
        if(event->state & GDK_CONTROL_MASK)
	    ctrlKey = JSVAL_TRUE;
        if(event->state & GDK_MOD1_MASK)
	    altKey = JSVAL_TRUE;
        
        JS_SetProperty(element->engine->cx, event_obj, "keyCode", &keyCode);
        JS_SetProperty(element->engine->cx, event_obj, "shiftKey", &shiftKey);
        JS_SetProperty(element->engine->cx, event_obj, "ctrlKey", &ctrlKey);
        JS_SetProperty(element->engine->cx, event_obj, "altKey", &altKey);
    }
    g_message("press keycode %d, state %d", keyCode, state);
    desktop_element_call_handler(element, "onkeypress");
    return FALSE;
}

static gboolean element_key_release_cb(GtkWidget *widget, GdkEventKey *event, gpointer ptr)
{
    desktop_element *element = (desktop_element *)ptr;
    guint16 keyCode = event->hardware_keycode;
    guint state = event->state;
    g_message("release keycode %d, state %d", keyCode, state);
    desktop_element_call_handler(element, "onkeyrelease");
    return FALSE;
}

static gboolean element_mouse_move_cb(GtkWidget *widget, GdkEventMotion *event, gpointer ptr)
{
    desktop_element *element = (desktop_element *)ptr;
    desktop_element_call_handler(element, "onmousemove");
    return FALSE;
}

static gboolean element_mouse_crossing_cb(GtkWidget *widget, GdkEventCrossing *event, gpointer ptr)
{
    desktop_element *element = (desktop_element *)ptr;
    if(event->type == GDK_ENTER_NOTIFY){
	desktop_element_call_handler(element, "onmouseover");
    }else if(event->type == GDK_LEAVE_NOTIFY){
	desktop_element_call_handler(element, "onmouseout");
    }
    return FALSE;
}

